#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <cerrno>
#include <sys/select.h>
#include "Sock.hpp"
#include "Err.hpp"
#include "Log.hpp"

namespace Yohifo
{
#define DEFAULT_EVENT (0x1>>1)
#define READ_EVENT (0x1<<0)
#define WRITE_EVENT (0x1<<1)
#define EXCEPT_EVENT (0x1<<2)


    struct FdEvent
    {
        int _fd;
        uint8_t _events;
        std::string clientIP;
        uint16_t clientPort;
    };

    // 多路转接服务器
    static const uint16_t gport = 8888;

    // 第三方数组，维护 fd
    // typedef int type_t;
    typedef FdEvent type_t;

    static const int N = (sizeof(fd_set) * 8);

    static const int defaultFd = -1;

    class SelectServer
    {
    public:
        SelectServer(const uint16_t &port = gport)
            : port_(port)
        {
            logMessage(Debug, "port: %d", port_);
        }

        ~SelectServer()
        {
            listen_sock_.Close();
        }

        void InitServer()
        {
            // 创建套接字、绑定、监听
            listen_sock_.Socket();
            listen_sock_.Bind(port_);
            listen_sock_.Listen();

            for (int i = 0; i < N; i++)
            {
                if(i == 0)
                {
                    fdarray_[i]._fd = listen_sock_.getFd(); // 这个是固定的
                    fdarray_[i]._events = READ_EVENT;    //读事件
                }
                else
                {
                    fdarray_[i]._fd = defaultFd;
                    fdarray_[i]._events = DEFAULT_EVENT;    //默认事件
                }

                fdarray_[i].clientIP = "";
                fdarray_[i].clientPort = 0;
            }

            logMessage(Debug, "socket、bind、listen Success");
        }

        void Accepter()
        {
            // listen 事件已响应，此时 accept 是不会被阻塞的
            std::string clientIP;
            uint16_t clientPort;
            int sock = listen_sock_.Accept(&clientIP, &clientPort);
            if (sock < 0)
                return;

            // 将收到的 sock 存入 fdarray 中
            int pos;
            for (pos = 1; pos < N; pos++)
                if (fdarray_[pos]._fd == defaultFd)
                    break;
            if (pos == N)
            {
                // fdarray 满了
                close(sock);
                logMessage(Warning, "Fdarray Full: [%d/%d]", pos, N);
            }
            else
            {
                // 存入 fdarray
                fdarray_[pos]._fd = sock;
                fdarray_[pos]._events = (READ_EVENT | WRITE_EVENT);
                fdarray_[pos].clientIP = clientIP;
                fdarray_[pos].clientPort = clientPort;

                DebugPrint();
            }
        }

        void Recver(int i)
        {
            // 已经响应的 fd，此时可以直接读取，并且保证此时读取是一定不会被阻塞的
            char buff[N];

            int n = recv(fdarray_[i]._fd, buff, sizeof(buff) - 1, 0);
            if (n > 0)
            {
                buff[n - 1] = 0; // 去掉 换行符
                std::cout << "client #: " << buff << std::endl;

                // 将进行数据回响
                std::string msg = buff;
                msg = "[server echo]: " + msg + "\n";

                send(fdarray_[i]._fd, msg.c_str(), msg.size(), 0);
            }
            else
            {
                if (n == 0)
                {
                    // 读取到末尾而结束
                    logMessage(Debug, "Recv end");
                }
                else
                {
                    // 读取失败
                    logMessage(Warning, "Recv fail");
                }

                // 关闭文件描述符、清理 fdarray
                int fd = fdarray_[i]._fd;
                close(fdarray_[i]._fd);
                fdarray_[i]._fd = defaultFd;
                fdarray_[i]._events = DEFAULT_EVENT;
                fdarray_[i].clientIP = "";
                fdarray_[i].clientPort = 0;

                DebugPrint();

                logMessage(Debug, "Close %d Success", fd);
            }
        }

        void Writer(int i)
        {
            //直接把客户端的相关信息发给它
            std::string msg;
            msg += "fd:" + std::to_string(fdarray_[i]._fd) + " ";
            msg += "event:" + std::to_string(fdarray_[i]._events) + " ";
            msg += "clientIP:" + fdarray_[i].clientIP + " ";
            msg += "clientPort:" + std::to_string(fdarray_[i].clientPort) + "\n";

            send(fdarray_[i]._fd, msg.c_str(), msg.size(), 0);
        }

        void HandlerEvent(fd_set &rfds, fd_set &wfds)
        {
            for (int i = 0; i < N; i++)
            {
                if (fdarray_[i]._fd == defaultFd)
                    continue;

                if(fdarray_[i]._events & READ_EVENT && FD_ISSET(fdarray_[i]._fd, &rfds))
                {
                    if(fdarray_[i]._fd == listen_sock_.getFd())
                        Accepter();
                    else
                        Recver(i);
                }
                
                if(fdarray_[i]._events & WRITE_EVENT && FD_ISSET(fdarray_[i]._fd, &wfds))
                {
                    if(fdarray_[i]._fd == listen_sock_.getFd())
                        Accepter();
                    else
                        Writer(i);
                }

                //TODO
            }
        }

        void StartServer()
        {
            // 在获取链接之前，需要将 fd 保存至 fd_set 中
            while (true)
            {
                // 更新 rfds
                fd_set rfds;
                fd_set wfds;

                FD_ZERO(&rfds);
                FD_ZERO(&wfds);

                int maxFd = 0;
                for (int i = 0; i < N; i++)
                {
                    if (fdarray_[i]._fd != defaultFd)
                    {
                        if(fdarray_->_events == READ_EVENT)
                            FD_SET(fdarray_[i]._fd, &rfds);
                        else if(fdarray_->_events == WRITE_EVENT)
                            FD_SET(fdarray_[i]._fd, &rfds);
                        else
                        {
                            //TODO
                        }

                        maxFd = max(maxFd, fdarray_[i]._fd);
                    }
                }

                int n = select(maxFd + 1, &rfds, &wfds, nullptr, nullptr);
                switch (n)
                {
                case 0:
                    logMessage(Debug, "timeout, %d %s", errno, strerror(errno));
                    break;
                case -1:
                    logMessage(Warning, "%d %s", errno, strerror(errno));
                    break;
                default:
                    logMessage(Info, "有一个事件就绪了 %d", n);
                    HandlerEvent(rfds, wfds);
                    break;
                }
            }
        }

        void DebugPrint()
        {
            // 打印 fdarray 信息
            std::cout << "==================================" << std::endl;
            std::cout << "fdarray: ";
            for (int i = 0; i < N; i++)
                if (fdarray_[i]._fd != defaultFd)
                    std::cout << fdarray_[i]._fd << " ";
            std::cout << std::endl
                      << "==================================" << std::endl;
        }

    private:
        Sock listen_sock_;
        uint16_t port_;
        type_t fdarray_[N];
    };
}